package lab2;

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.math.BigInteger;
import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class CalcThread implements Runnable {

    private BigInteger ergebnis = BigInteger.ONE;
    private boolean running;
    private BigInteger cycles;
    private BigInteger n; // number to factorize
    private BigInteger c;
    private Worker owner;
    public Lock runlock;
    private ThreadMXBean threadMXBean;

    public CalcThread(BigInteger n, BigInteger c, Worker w) {
        this.running = true;
        this.n = n;
        this.c = c;
        this.owner = w;
        runlock = new ReentrantLock();
    }

    private BigInteger bigIntCalc1(BigInteger a, BigInteger n, BigInteger x) {
        x = ((x.multiply(x)).add(a)).mod(n);
        return x;
    }// bigIntCalc1

    public void run() {
        threadMXBean = ManagementFactory.getThreadMXBean();
        threadMXBean.setThreadContentionMonitoringEnabled(true);
        threadMXBean.setThreadCpuTimeEnabled(true);
        //compute exit condition (n/2)
        BigInteger halfN = n.divide(new BigInteger("2"));
        if (running) {
            cycles = BigInteger.ZERO;
            if (n.equals(new BigInteger("2"))) {
                ergebnis = BigInteger.ZERO;
            }// if
            if (n.mod(new BigInteger("2")).equals(BigInteger.ZERO)) {
                ergebnis = new BigInteger("2");
            }// if
            if (ergebnis.equals(BigInteger.ONE)) {

                BigInteger x = new BigInteger(n.bitLength() - 1, new Random());
                BigInteger y = x;
                BigInteger p = BigInteger.ONE;
                BigInteger d;
                cycles = BigInteger.ONE;
                do {
                    cycles = cycles.add(BigInteger.ONE);
                    x = bigIntCalc1(c, n, x);
                    y = bigIntCalc1(c, n, y);
                    y = bigIntCalc1(c, n, y);
                    d = (y.subtract(x)).mod(n);
                    p = d.abs().gcd(n.abs());
                    // exit condition
                    if (cycles.compareTo(halfN) > 0) {
                        System.out.println("Exiting rho-cycling. Too large amount of time needed!");
                        break;
                    }
                } while (p.equals(BigInteger.ONE));

                if (!p.equals(n)) {
                    ergebnis = p;
                } else {
                    ergebnis = BigInteger.ZERO;
                }// else
            }// if
        }// running

        // immernoch am laufen?
        runlock.lock();
        if (running) {
            if (owner.threadRunning) {
                owner.getResult(ergebnis, n, cycles, threadMXBean.getCurrentThreadCpuTime() / 1000000);
            }// if
            this.terminate();
            owner.threadRunning = false;
        }// if
        runlock.unlock();
    }// run

    public void terminate() {
        this.running = false;
    }// terminate

    public boolean getRunning() {
        if (running) {
            return true;
        } else
            return false;
    }// getRunning

    public void setRunning(boolean running) {
        this.running = running;
    }// setRunning
}// CalcThread
